package com.itheima.pinda.execute;

import com.itheima.pinda.common.utils.SpringContextUtils;
import com.itheima.pinda.entity.ScheduleJobEntity;
import com.itheima.pinda.entity.ScheduleJobLogEntity;
import com.itheima.pinda.service.ScheduleJobLogService;
import com.itheima.pinda.utils.ExceptionUtils;
import com.itheima.pinda.utils.IdUtils;
import com.itheima.pinda.utils.ScheduleUtils;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.lang.reflect.Method;
import java.util.Date;

/**
 * 定时任务, 进行智能调度操作
 */
@Slf4j
public class ScheduleJob extends QuartzJobBean {


    @Override
    protected void executeInternal(JobExecutionContext context) {
        ScheduleJobEntity scheduleJob =
                (ScheduleJobEntity) context.getMergedJobDataMap().
                        get(ScheduleUtils.JOB_PARAM_KEY);
        log.info("定时任务开始执行..., 定时任务id: {}", scheduleJob.getId());

        // 记录定时任务相关的日志信息
        // 封装日志对象
        ScheduleJobLogEntity jobLogEntity = new ScheduleJobLogEntity();
        jobLogEntity.setId(IdUtils.get());
        jobLogEntity.setJobId(scheduleJob.getId());
        jobLogEntity.setBeanName(scheduleJob.getBeanName());
        jobLogEntity.setParams(scheduleJob.getParams());
        jobLogEntity.setCreateDate(new Date());

        long startTime = System.currentTimeMillis();
        try {
            // 核心调度逻辑 - 通过反射调用目标对象, 在目标对象中封装智能调度核心逻辑
            log.info("定时任务执行中, 任务id为: {}", scheduleJob.getId());
            // 获取目标对象
            Object target = SpringContextUtils.getBean(scheduleJob.getBeanName());
            // 反调获取 target的 run方法  getDeclaredMethod可以获取当前类和父类所有public方法
            Method declaredMethod = target.getClass().getDeclaredMethod("run", String.class, String.class, String.class, String.class);
            // 通过反射调用目标对象的方法
            declaredMethod.invoke(target, scheduleJob.getBusinessId(),
                    scheduleJob.getParams(),
                    scheduleJob.getId(),
                    jobLogEntity.getId());

            jobLogEntity.setStatus(1);  // 成功
        } catch (Exception e) {
            jobLogEntity.setStatus(0);  // 失败
            jobLogEntity.setError(ExceptionUtils.getErrorStackTrace(e));
            log.error("定时任务执行失败, 任务id为: {}", scheduleJob.getId());
        } finally {
            int times = (int) (System.currentTimeMillis() - startTime);
            jobLogEntity.setTimes(times);   // 耗时
        }

        // 调度任务这个对象, 每次执行一次就会创建一个对象, 所以不使用 @Resource + @Component 将ScheduleJobLog注入进来
        // 所以这里使用 Spring工具类的方式, 将 JobLogService注入进来
        ScheduleJobLogService jobLogService = SpringContextUtils.getBean(ScheduleJobLogService.class);
        jobLogService.save(jobLogEntity);
    }
}